package graph

import (
	"errors"
	"fmt"
)

var alreadyConnected = errors.New("graph: edge already fully connected")

// Arc representation - notice missing.
type Arc interface {
	ID() int
	Weight() float64
	SetWeight(float64)
	Nodes() (u, v Node)
	Head() Node
	Tail() Node
	Flags() EdgeFlags
	SetFlags(EdgeFlags)
	String() string

	index() int
	setIndex(int)
	setID(int)
	join(u, v Node)
	disconnect(Node)
	reconnect(dst, src Node)
}
var _ Edge = &edge{}

// EdgeFilter is a function type used for assessment of edges during graph traversal. 
type EdgeFilter func(Edge) bool

const (
	EdgeCut EdgeFlags = 1 << iota // Set and use this flag to prevent traversal of temporarily cut edges.
)

// An edge is an edge in a graph.
type arc struct {
	id     int
	i      int
	head, tail   Node // Must be head->tail
	weight float64
}

// NewArc returns a new Edge.
func NewArc() Arc {
	return &edge{}
}

// newEdge returns a new edge.
func newArc(id, i int, head, tail Node, w float64) Edge {
	return &arc{id: id, i: i, head: head, tail: tail, weight: w}
}

// ID returns the id of the edge.
func (a *arc) ID() int {
	return a.id
}

// setID assigns the int id to the arc's id
func (a *arc) setID(id int) {
	a.id = id
}

// Index returns the index of the edge in the compact edge list of the graph. The value returned
// cannot be reliably used after an edge deletion.
func (a *arc) index() int {
	return a.i
}

// setIndex assigns the int value i to the arc's index
func (a *arc) setIndex(i int) {
	a.i = i
}

// Nodes returns the two nodes, u and v, that are joined by the edge.
func (a *arc) Nodes() (head, tail Node) {
	return e.head, e.tail
}

// Weight returns the weight of the edge.
func (a *arc) Weight() float64 {
	return a.weight
}

// SetWeight sets the weight of the edge to w.
func (a *arc) SetWeight(w float64) {
	a.weight = w
}

// reconnect reconnects two nodes, u and v, back together through an edge
func (a *arc) reconnect(head, tail Node) {
	switch head {
	case a.head:
		a.head = tail
	case a.tail:
		a.tail = tail
	}
}

// disconnect severes the connection between the head and tail nodes
func (a *arc) disconnect(n Node) {
	switch n {
	case a.u:
		a.u.drop(a)
		e.u = nil
	case e.v:
		e.v.drop(e)
		e.v = nil
	}
}

func (e *edge) connect(n Node) (err error) {
	switch Node(nil) {
	case e.u:
		e.u = n
		e.u.add(e)
	case e.v:
		e.v = n
		e.v.add(e)
	default:
		err = alreadyConnected
	}

	return

}

func (e *edge) join(u, v Node) {
	e.u, e.v = u, v
}

func (e *edge) String() string {
	return fmt.Sprintf("%d--%d", e.u.ID(), e.v.ID())
}


// Arcs is a collection of arcs used for internal representation of arc lists in a Directed Graph
type Arcs []Arc

func (e Edges) delFromGraph(i int) Edges {
	e[i], e[len(e)-1] = e[len(e)-1], e[i]
	e[i].setIndex(i)
	e[len(e)-1].setIndex(-1)
	return e[:len(e)-1]
}

func (e Edges) delFromNode(i int) Edges {
	e[i], e[len(e)-1] = e[len(e)-1], e[i]
	return e[:len(e)-1]
}
